package com.study.zookeeper.service;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.concurrent.*;

@Service
public class LockService {

    @Autowired
    public CuratorFramework curatorClient;

    //推荐使用ConnectionStateListener监控连接的状态，因为当连接LOST时你不再拥有锁

    /**
     * 分布式可重入排他锁
     * 可重入：一个客户端在拥有锁的时候，可以重复获取，不会被阻塞
     */
    public void interProcessMutex() {
        //定义两个线程
        final CountDownLatch countDownLatch = new CountDownLatch(2);
        //定义一个锁
        final InterProcessLock lock = new InterProcessMutex(curatorClient, "/home/wufaliang/abc");
        //第一个线程获取锁
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock.acquire();
                    System.out.println("1获取到了锁");
                    lock.acquire();
                    System.out.println("1重新去获取锁");
                    Thread.sleep(5 * 1000);
                    lock.release();
                    System.out.println("1释放锁");
                    lock.release();
                    System.out.println("1再次释放锁");

                    countDownLatch.countDown();
                } catch (Exception e) {

                }
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock.acquire();
                    System.out.println("2获取锁");
                    lock.acquire();
                    System.out.println("2在此获取锁");
                    Thread.sleep(5 * 1000);
                    lock.release();
                    System.out.println("2释放锁");
                    lock.release();
                    System.out.println("2再次释放锁");

                    countDownLatch.countDown();
                }catch (Exception e) {

                }
            }
        }).start();
        try {
            countDownLatch.await();
        } catch (Exception e) {

        }
        System.out.println("主线程执行结束....");
    }

    /**
     * 分布式排他锁（不可重入 ）
     */
    public void interProcessSemaphoreMutex() {
        //定义一个锁
        InterProcessSemaphoreMutex lock = new InterProcessSemaphoreMutex(curatorClient, "/home/wufaliang/abc");
        //定义一个线程池
        ExecutorService executors = Executors.newFixedThreadPool(2);
        for(int i=0; i<2; i++) {
            Callable<Void> task = new Callable<Void>() {
                @Override
                public Void call() throws Exception {
                    try {
                        lock.acquire();
                        System.out.println(Thread.currentThread().getName() + "获取到了锁");
                        lock.acquire();
                        System.out.println(Thread.currentThread().getName() + "重新去获取锁");
                        Thread.sleep(5 * 1000);
                        lock.release();
                        System.out.println(Thread.currentThread().getName() + "释放锁");
                        lock.release();
                        System.out.println(Thread.currentThread().getName() + "再次释放锁");
                    } catch (Exception e) {

                    }
                    return null;
                }
            };
            executors.submit(task);
        }
        executors.shutdown();
        try{
            executors.awaitTermination(20, TimeUnit.SECONDS);
        } catch(Exception e) {

        }
        System.out.println("主线程执行结束");
    }

    /**
     * 分布式读写锁
     * 读锁和读锁不互斥，只要是写锁就互斥
     */
    public void interProcessReadWriteLock() {
        //定义一个锁
        final InterProcessReadWriteLock lock = new InterProcessReadWriteLock(curatorClient, "/home/wufaliang/abc");
        final InterProcessLock readLock = lock.readLock();
        final InterProcessLock writeLock = lock.writeLock();
        //定义一个线程池
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        for(int i =0; i<2;i++){
            Callable<Void> task = new Callable<Void>() {
                @Override
                public Void call() throws Exception {
                    //readLock.acquire();
                    writeLock.acquire();
                    System.out.println(Thread.currentThread().getName() + "获取锁");
                    //readLock.acquire();
                    writeLock.acquire();
                    System.out.println(Thread.currentThread().getName() + "再次获取锁");
                    Thread.sleep(5 * 1000);
                    //readLock.release();
                    writeLock.release();
                    System.out.println(Thread.currentThread().getName() + "释放锁");
                    //readLock.release();
                    writeLock.release();
                    System.out.println(Thread.currentThread().getName() + "再次释放锁");
                    return null;
                }
            };
            executorService.submit(task);
        }
        executorService.shutdown();
        try {
            executorService.awaitTermination(20, TimeUnit.SECONDS);
        } catch (Exception e) {}
        System.out.println("系统执行结束");
    }

    /**
     * 将多个锁作为单个实体管理的容器
     */
    public void interProcessMultiLock() {
        //不可重入锁
        final InterProcessSemaphoreMutex lock1 = new InterProcessSemaphoreMutex(curatorClient, "/home/wufaliang/abc");
        //可重入锁
        final InterProcessMutex lock2 = new InterProcessMutex(curatorClient, "/home/wufaliang/abc");
        final InterProcessMultiLock locks = new InterProcessMultiLock(Arrays.asList(lock1, lock2));
        final CountDownLatch countDownLatch = new CountDownLatch(1);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //获取集合中所有的锁
                    locks.acquire();
                    //因为集合中存在一个不可重入的锁，锁整个集合不可重入
                    System.out.println(locks.acquire(2, TimeUnit.SECONDS));
                    //获取可重入锁
                    System.out.println(lock2.acquire(2, TimeUnit.SECONDS));
                    //获取一个不可重入锁，会失败
                    System.out.println(lock1.acquire(2, TimeUnit.SECONDS));

                    countDownLatch.countDown();
                } catch (Exception e) {

                }
            }
        }).start();
        try {
            countDownLatch.await();
        } catch (Exception e) {

        }
        System.out.println("task is done");

    }

}
